自从我的新书 前端自动化测试框架 – Cypress从入门到精通上市以来,这本书受到了大量同学热情的追捧和讨论。在跟同学们的交流中,我也了解到, 原来除了国外优秀的公司(例如Adobe, 迪士尼,AutoDesk等等), 国内也有很多公司在尝试使用Cypress提升测试效率。而在Cypress中国群内、在公众号iTesting里,我每天都能看到大量关于Cypress的使用讨论和私下问询。这让我感到无比荣幸。(买了书的同学们,公众号回复你的微信,拉你到Cypress中国群)。
除了日常推荐大家通过阅读我的书来解决日常Cypress使用问题外,我也一直在更新着我这边的Cypress知识图谱, 不夸张的说,目前我总结和实践下来知识点多达200多篇。本着“雕琢自我,普惠他人”的原则,我决定在公众号iTesting上开设<你不知道的Cypress系列>专栏。此专栏目的是分享一些我自己趟过的坑,走过的弯路、以及在选型时抛弃了的实践。希望让大家在选用Cypress作为前端自动化测试框架方案时, 可以借鉴一下,避免再走我走过的弯路。
今天是<你不知道的Cypress系列>的第三篇 – 是时候重构自己的思维了!
由于Selenium/WebDriver的“荼毒”, 当前在自动化过程中,很多不合理的操作,反而都变成了标准流程。
例如,要进行元素属性值比较,我们首先想到的就是先赋值,再比较。
再比如,自动化过程中,常常会陷入”条件测试(Conditional Testing)“的陷阱! 例如,我见过太多这样的case:”如果我点击了某button,如果弹出框没有出现,我执行A操作,如果出现,我执行B操作“。
由于这种“荼毒”,初次使用Cypress时, 大多数同学都会认为自己掉进坑里了!
(一)诡异的赋值
01 赋值不起作用
赋值操作是最常见的了,赋值最常用的场景是获取元素的某个属性供以后使用。
没接触过JavaScript的同学,在第一次写Cypress脚本时,一定会遇见如下问题:
如上述代码所示,我定义了一个变量name,并尝试把submit button的text即“Submit” 赋值给name。 上面的代码看起来没有任何毛病,但是运行时,你会发现我第一次打印时有值, 但是二次打印时name的值是null。
02 赋值不起作用的原因
写惯了Python或Java的同学往往会卡在这里觉得莫名其妙。其实也就是同步执行和异步执行的差异了。
同步执行:
可以简单理解为,当你执行一个操作,在这个操作没有结果之前,其后续的操作不会执行。
异步执行:
可以简单理解为,当你执行一个操作后,其后续的操作可以立即执行, 当这个操作有结果后,再通过状态,通知或者回调来通知这个操作的调用方。
这个解释相当之不准确,不过也足够我们继续下面的内容了。 你如果感兴趣, 可以搜索同步、异步、阻塞、非阻塞来了解更多进程通信和系统调用的知识。
正常情况下,Python代码,Java代码就是同步执行的,JavaScript代码就是异步执行的。
了解了这一点,你就明白了,当执行到第13行时,name的值还没有被返回,所以打印不出来。
03 99%的情况都无须赋值
使用Selenium/WebDriver比较熟悉的同学,初次转到Cypress后,很容易就自无劝退:”Cypress好难用, 我还是用回Selenium/WebDriver“吧。
拿对元素属性值进行断言为例,大家很容易就沿用Selenium/WebDriver时代的旧思维,认为,必须先拿出元素的属性值赋给一个变量,然后在用这个变量跟给定的期望结果对比。实际上,根本无需如此!
在Cypress中,99%的操作都无须赋值!
下面分别举例:
Selenium/WebDriver
|
|
Cypress:
|
|
就这么简单!
(二)Cypress命令是如何运行的?
01
先来看一个大家常常会犯的错误:
假设我们定义了一个自定义方法login,最后返回登录后的凭证:
然后在测试用例里,经常看到这样的使用方式:
最后会发现auth的值是undefined,很多人百撕不得骑姐。
为什么?
这是因为Cypress命令在它们被调用时不会执行任何操作。它们会自我排队(“enqueue themselves”),最后在统一运行。来看下官方的解释:
这个就是Cypress的魔力。这就是为什么JavaScript是异步执行的,但是Cypress命令却能按照你的代码“顺序“执行的原因!
01
那么,知道了Cypress命令是如何运行的,再来看上面的登录例子,你就知道了,
cy.login没有被执行,当然auth里就没有值了。
那么,如何才能确保cy.login被执行呢?
为了让你能够访问到Cypress命令执行的结果,Cypress提供了
.then是闭包的一个典型应用。(如果想深入理解,可以查下Promise)
所以,这下,我们知道要访问cy.login的返回值,我们应该这样用:
这下,你就能愉快的使用Cypress命令的返回值了,不过也带来一个问题,就是代码层次比较深。。。
(三)拒绝条件测试
01
前面我提到了条件测试(Conditional Testing),实际上,条件测试常见常景如下:
- 我想在元素存在或者不存在时,执行不同的操作。
- 我的应用程序有A/B Testing,我需要测试到不同的分支。
为了实现这个功能,在Selenium/WebDriver编程中,我们大量使用if…else,我们以为我们Cover住这种情况了,结果我们就发现我们的测试会薛定谔成功:有时候执行能成功,有时候执行不成功, 在你不执行的时候你永远不知道到底执行能不能成功。
我们来看一个例子:
假设你的应用程序代码如下:
你的测试代码就会如下:
这也是Selenium/WebDriver被诟病的原因之一,不稳定!
02
为了避免这个情况,Cypress告诉你, 不要去做条件测试(Conditional Testing)!
Cypress说,既然你在测试,那么你就应该知道你的每一步下去,其结果是什么。如果你不能确定你的操作下去结果是什么,那么你就不是在测试!(没毛病吧)
相应的,你就要调整你的测试策略,尽量避免让自己的代码处于条件测试(Conditional Testing)下, 具体来说就一句话:
事先做一些操作,确保你的某个操作一定只有一个结果!
如何做到呢?别忘记,Cypress是运行在浏览器之内的,是跟你的应用程序运行在同一个生命周期的,你对你的应用程序有完全的控制权!
听起来很好,不过很可惜。这句话主要是对开发说的,对我们QA来说,用处不大(因为我们QA还是不知道改哪行代码啊!)。
不过,这里还是有一些原则, 比如:
- A/B Testing, 可以根据AB的策略,构造出一定会走A逻辑的测试数据。
- 判断元素在不在,一定可以根据业务知道你的什么操作,它一定会在。
总结
当你初次使用Cypress时,特别是当你是从Selenium/WebDrvier转到Cypress来时,你一定会感觉到不习惯。这是必然的。
当你遇见问题时,不妨尝试转换下思维,把老的思维模式抛弃掉,转入到Cypress的思维中来,毕竟,我们做测试是为了: